package com.avecsys.quotemanager.dao.common;

import static org.hibernate.criterion.Example.create;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException;

import com.avecsys.quotemanager.exception.DAOException;
import com.avecsys.quotemanager.pojos.BasePojo;

public class BaseDAOImpl extends HibernateTemplateManagerDAO implements BaseDAO {

	private static final Log log = LogFactory.getLog(BaseDAOImpl.class);

	@Override
	public Integer save(final BasePojo persistantPojo) throws DAOException {
		Integer transactionStatus = EnumTransactionStatus.SUCESS.ordinal();
		try {
			getHibernateTemplate().execute(new HibernateCallback<Object>() {
				@Override
				public Object doInHibernate(Session session)
						throws HibernateException, SQLException {
					session.save(persistantPojo);
					return persistantPojo;
				}
			});
		} catch (Exception e) {
			log.error(e);
			transactionStatus = EnumTransactionStatus.FAIL.ordinal();
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	@Override
	public Integer update(final BasePojo persistantPojo) throws DAOException {
		Integer transactionStatus = EnumTransactionStatus.SUCESS.ordinal();
		try {
			getHibernateTemplate().execute(new HibernateCallback<Object>() {
				@Override
				public Object doInHibernate(Session session)
						throws HibernateException, SQLException {
					session.update(persistantPojo);
					return persistantPojo;
				}
			});
		} catch (HibernateOptimisticLockingFailureException holfe) {
			log.error(holfe);
			transactionStatus = EnumTransactionStatus.OPTIMIST_LOCK_FAIL
					.ordinal();
		} catch (Exception e) {
			e.printStackTrace();
			log.error(e);
			transactionStatus = EnumTransactionStatus.FAIL.ordinal();
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	@Override
	public <T> List findAll(final T basePojo) throws DAOException {
		try {
			return getHibernateTemplate().executeFind(
					new HibernateCallback<List<BasePojo>>() {
						@Override
						public List<BasePojo> doInHibernate(
								final Session session)
								throws HibernateException, SQLException {
							@SuppressWarnings("unchecked")
							final List<BasePojo> results = (List<BasePojo>) session
									.createCriteria(basePojo.getClass())
									.add(create(basePojo)).list();
							log.debug("findAll successful, result size: "
									+ results.size());
							return results;
						}
					});
		} catch (Exception e) {
			log.error("find by example failed", e);
			throw new DAOException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T findById(final T instance, final Long pk) throws DAOException {
		try {
			return (T) getHibernateTemplate().execute(
					new HibernateCallback<T>() {
						@Override
						public T doInHibernate(Session session)
								throws HibernateException, SQLException {
							return (T) session.get(instance.getClass(), pk);
						}
					});
		} catch (Exception e) {
			log.error("find by id failed", e);
			throw new DAOException(e);
		}
	}

	/**
	 * Method to delete the persisted object
	 * 
	 * @param instance
	 * @return An Integer Status code value
	 * @throws DAOException
	 */
	@Override
	public <T> Integer delete(final T instance) throws DAOException {
		Integer transactionStatus = EnumTransactionStatus.SUCESS.ordinal();
		try {
			getHibernateTemplate().execute(new HibernateCallback<T>() {
				@Override
				public T doInHibernate(Session session)
						throws HibernateException, SQLException {
					session.delete(instance);
					return instance;
				}
			});
		} catch (Exception e) {
			log.error(e);
			transactionStatus = EnumTransactionStatus.FAIL.ordinal();
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	@Override
	public <T> Integer markAsDeleted(final T instance) throws DAOException {
		Integer transactionStatus = EnumTransactionStatus.SUCESS.ordinal();
		try {
			getHibernateTemplate().execute(new HibernateCallback<Object>() {
				@Override
				public Object doInHibernate(Session session)
						throws HibernateException, SQLException {
					session.update(instance);
					return instance;
				}
			});
		} catch (Exception e) {
			log.error(e);
			transactionStatus = EnumTransactionStatus.FAIL.ordinal();
			throw new DAOException(e);
		}
		return transactionStatus;
	}

	/**
	 * Method to get all active record instance
	 * 
	 * @param instance
	 * @return List of Persisted Instances
	 * @throws DAOException
	 */
	@Override
	public <T> List findAllActive(final T instance) throws DAOException {
		try {
			((BasePojo) instance).setStatus(EnumDBRecordStatus.ACTIVE.status());
			return getHibernateTemplate().executeFind(
					new HibernateCallback<List<BasePojo>>() {
						@Override
						public List<BasePojo> doInHibernate(
								final Session session)
								throws HibernateException, SQLException {
							@SuppressWarnings("unchecked")
							final List<BasePojo> results = (List<BasePojo>) session
									.createCriteria(instance.getClass())
									.add(create(instance)).list();
							log.debug("findAll successful, result size: "
									+ results.size());
							return results;
						}
					});
		} catch (Exception e) {
			log.error("findAllActive failed", e);
			throw new DAOException(e);
		}
	}

	@Override
	public <T> List findAllActiveByCriteria(final T instance,
			final Map<String, Object> mapCriteria) throws DAOException {
		try {
			((BasePojo) instance).setStatus(EnumDBRecordStatus.ACTIVE.status());
			return getHibernateTemplate().executeFind(
					new HibernateCallback<List<BasePojo>>() {
						@Override
						public List<BasePojo> doInHibernate(
								final Session session)
								throws HibernateException, SQLException {
							final Criteria criteria = session
									.createCriteria(instance.getClass());
							for (final String columnName : mapCriteria.keySet()) {
								final Object value = mapCriteria
										.get(columnName);
								criteria.add(Expression.eq(columnName, value));
							}
							criteria.add(Expression.eq("status",
									EnumDBRecordStatus.ACTIVE.status()));
							final List<BasePojo> results = criteria.list();
							log.debug("findAllActiveByCriteria successful, result size: "
									+ results.size());
							return results;
						}
					});
		} catch (Exception e) {
			log.error("findAllActiveByCrieria failed", e);
			throw new DAOException(e);
		}
	}
}
